#pragma once

#include <vex/core/handle_policy.h>
#include <vex/core/as_any.h>
#include <vex/core/as_intrusive.h>
#include <vex/iterable/any/implements_range_concept_policy.h>
#include <vex/iterable/contract/range.h>
#include <type_traits>

namespace vex { 
    namespace iterable {
        template<class TCategory = trivial, class TRef = void>
        struct any_range_traits {
            /// Required by implemented policies
            typedef contract::range<TCategory, TRef> contract_type;
            typedef boost::intrusive_ptr<contract_type> holder_type;
            typedef contract_type* item_type;

            /// Range specific traits:
            typedef TCategory category;
            typedef TRef reference;
            typedef typename std::remove_reference<reference>::type value_type;
            typedef typename std::add_pointer<value_type>::type pointer;
        };

        template <class TCategory = trivial_tag, class TRef = void>
        class any_range : public core::handle_policy<
            any_range<TCategory, TRef>, 
            any_range_traits<TCategory, TRef>,
            any::implements_range_concept_policy<
                any_range<TCategory, TRef>, 
                any_range_traits<TCategory, TRef> 
            >
        > 
        {
            // in order to support conversions other ranges need to be befriended
            template<class, class> friend class any_range;
        public:
            VEX_ANY_HANDLE_DEFAULT_BODY_IMPL(
                any_range,
                typename BOOST_IDENTITY_TYPE((any_range_traits<TCategory, TRef>)),
                VEX_TRAITS_IS_DEPENDENT_NAME);

            /// <summary>
            /// Supports implicit conversion from <c>any_range</c> of compatible categories
            /// </summary>
            template<class UCategory, class URef> 
            any_range(any_range<UCategory, URef> const & p_other, typename std::enable_if<
                std::is_convertible<UCategory, TCategory>::value &&
                std::is_same<URef, TRef>::value
            >::type* = 0)
                : m_holder(p_other.m_holder) {}

            /// <summary>
            /// Supports assignment from <c>any_range</c> of compatible categories
            /// </summary>
            template<class UCategory, class URef> 
            auto operator = (any_range<UCategory, URef> const & p_other)
                -> typename std::enable_if<
                        std::is_convertible<UCategory, TCategory>::value &&
                        std::is_same<URef, TRef>::value, 
                   any_range&>::type
            {
                any_range(p_other).swap(*this);
                return *this;
            }
        private:
            holder_type m_holder;
        };

        /// adl support, must be in the same namespace as contract
        namespace contract {
            template<class TCategory, class TRef> auto adl_as_any_from_out(range<TCategory, TRef>* p_handle)
                -> any_range<TCategory, TRef>
            {
                return any_range<TCategory, TRef>(p_handle, false);
            }

            template<class TCategory, class TRef> auto adl_as_any_from_in(range<TCategory, TRef>* p_handle)
                -> any_range<TCategory, TRef>
            {
                return any_range<TCategory, TRef>(p_handle, true);
            }
        }
    }

    using iterable::any_range;
}